package com.party.mem.biz.member;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.party.core.model.BaseModel;
import com.party.core.model.commune.CRoute;
import com.party.core.model.commune.CRouteInfo;
import com.party.core.service.commune.ICRouteInfoService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 路线业务
 *
 * @Date: 2019/10/23 9:28
 */
@Service
public class MemberRouteBizService {

    public static  final  String ak = "QH3YaMajiKeNrOMGnF27zddTcPbIry76";
    public static  final  Integer TRAVEL_TYPE_TRANSIT = 0; //公交
    public static  final  Integer TRAVEL_TYPE_DIRVE = 1;    //驾驶
    public static  final  Integer ROUTE_TYPE_ORIGIN = 0;        //路线起点
    public static  final  Integer ROUTE_TYPE_DESTINATION = 1;  //路线终点
    public static  final  Integer ADDRESS_TYPE_COMPANY = 0;  //公司地址
    public static  final  Integer ADDRESS_TYPE_HOME = 1;  //现居地址

    @Autowired
    private ICRouteInfoService routeInfoService;

    static Logger logger = LoggerFactory.getLogger(MemberRouteBizService.class);



    /***
     * 通过地址获取对应的坐标
     * @Date 9:40 2019/10/23
     * @param address
     * @return
     **/
    public String getCoordinate(String address) {
        String result = "";
        String coordinate = "";
        try {
            String requestUrl = "http://api.map.baidu.com/geocoding/v3/?address=" + address + "&output=json&ak=" + ak + "&callback=renderReverse";
            result = sendGet(requestUrl);
            if (StringUtils.isNotEmpty(result)) {
                result = result.replace("renderReverse&&renderReverse(", "").replace(")", "");
            }
            JSONObject jsonObject = JSONObject.parseObject(result);
            Integer status = (Integer) jsonObject.get("status");
            String msg = (String) jsonObject.get("msg");
            if (Integer.valueOf("0").equals(status)) {
                Map<String, Map<String, BigDecimal>> result1 = (Map<String, Map<String, BigDecimal>>) jsonObject.get("result");
                Map<String, BigDecimal> location = result1.get("location");
                BigDecimal lng = location.get("lng");
                BigDecimal lat = location.get("lat");
                System.out.println(lat + "," + lng);
                coordinate = lat + "," + lng;
            } else {
                logger.error("地址转换失败",msg);
            }
        } catch (Exception e) {
            logger.error("获取地址对应经纬度失败", e);
        }

        return coordinate;

    }

    public static String sendGet(String url) {
        String result = "";
        BufferedReader in = null;
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection con = realUrl.openConnection();
            // 设置通用的请求属性
            con.setRequestProperty("accept", "*/*");
            con.setRequestProperty("connection", "Keep-Alive");
            con.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            con.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = con.getHeaderFields();
            // 遍历所有的响应头字段
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));// 防止乱码
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            logger.error("发送GET请求出现异常！", e);
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }


    /***
     * 计算时间和距离
     * @Date 10:15 2019/10/24
     * @param route
     * @param allCompanyAddress
     * @param homeAddress
     * @return
     **/
    public boolean calDistanceAndTime(CRoute route, List<Map<String, Object>> allCompanyAddress, List<Map<String, Object>> homeAddress,String partnerId)
    {
        long startTime = System.currentTimeMillis();
        System.out.println("开始获取时间"+startTime);

        List<Map<String,String>> companyCoordinate = new ArrayList<>();

        List<Map<String,String>> homeCoordinate = new ArrayList<>();

        //获取公司地址坐标
        for (Map<String, Object> companyAddress : allCompanyAddress) {
            Map<String,String> cMap = new HashMap<>();
            String coordinate =  companyAddress.get("cLat") +","+ companyAddress.get("cLng");
            cMap.put("coordinate",coordinate);
            cMap.put("memberId",(String) companyAddress.get("memberId"));
            companyCoordinate.add(cMap);
        }

        //获取居住地址坐标
        for (Map<String, Object> homeAddres : homeAddress) {
            Map<String,String> hMap = new HashMap<>();
            String coordinate =  homeAddres.get("hLat") +"," +homeAddres.get("hLng");
            hMap.put("coordinate",coordinate);
            hMap.put("memberId",(String) homeAddres.get("memberId"));
            homeCoordinate.add(hMap);
        }

        //获取公司到起点的距离--驾驶
        new Thread(new Runnable() {
            @Override
            public void run() {
                getAllCouteInfo(partnerId, companyCoordinate, TRAVEL_TYPE_DIRVE, route, ROUTE_TYPE_ORIGIN, ADDRESS_TYPE_COMPANY);
            }
        },"getAllCouteInfo1").start();


        // 获取公司到起点的距离--公交
        new Thread(new Runnable() {
            @Override
            public void run() {
                getDestinAndTimeFromTransit(partnerId,companyCoordinate,TRAVEL_TYPE_TRANSIT,route,ROUTE_TYPE_ORIGIN,ADDRESS_TYPE_COMPANY);
            }
        },"getAllCouteInfo2").start();


        //获取终点到公司的距离--驾驶
        new Thread(new Runnable() {
            @Override
            public void run() {
                getAllCouteInfo(partnerId, companyCoordinate, TRAVEL_TYPE_DIRVE, route, ROUTE_TYPE_DESTINATION, ADDRESS_TYPE_COMPANY);
            }
        },"getAllCouteInfo3").start();


        //获取终点到公司的距离--公交
        new Thread(new Runnable() {
            @Override
            public void run() {
                getDestinAndTimeFromTransit(partnerId,companyCoordinate,TRAVEL_TYPE_TRANSIT,route,ROUTE_TYPE_DESTINATION,ADDRESS_TYPE_COMPANY);
            }
        },"getAllCouteInfo4").start();


        //获取居住点到起点的距离--驾驶
        new Thread(new Runnable() {
            @Override
            public void run() {
                getAllCouteInfo(partnerId, homeCoordinate, TRAVEL_TYPE_DIRVE, route, ROUTE_TYPE_ORIGIN, ADDRESS_TYPE_HOME);
            }
        },"getAllCouteInfo5").start();



        //获取居住点到起点的距离--公交
        new Thread(new Runnable() {
            @Override
            public void run() {
                getDestinAndTimeFromTransit(partnerId,homeCoordinate,TRAVEL_TYPE_TRANSIT,route,ROUTE_TYPE_ORIGIN,ADDRESS_TYPE_HOME);
            }
        },"getAllCouteInfo6").start();


        //获取终点到居住点的距离--驾驶
        new Thread(new Runnable() {
            @Override
            public void run() {
                getAllCouteInfo(partnerId, homeCoordinate, TRAVEL_TYPE_DIRVE, route, ROUTE_TYPE_DESTINATION, ADDRESS_TYPE_HOME);
            }
        },"getAllCouteInfo7").start();


        //获取终点到居住点的距离--公交
        new Thread(new Runnable() {
            @Override
            public void run() {
                getDestinAndTimeFromTransit(partnerId,homeCoordinate,TRAVEL_TYPE_TRANSIT,route,ROUTE_TYPE_DESTINATION,ADDRESS_TYPE_HOME);
            }
        },"getAllCouteInfo8").start();

        long endTime = System.currentTimeMillis();
        System.out.println("结束时间"+endTime);

        System.out.println("耗时时间"+(endTime - startTime));

        return true;
    }

    private boolean getDestinAndTimeFromTransit(String partnerId,List<Map<String,String>> coordinates,Integer travelType,
                                                          CRoute route,Integer routeFlag,Integer addressType)
    {
        List<CRouteInfo> cRouteInfoList = new ArrayList<>();
        String origin = "";
        if (ROUTE_TYPE_ORIGIN.equals(routeFlag))
        {
            origin = route.getOrigin();
        } else if (ROUTE_TYPE_DESTINATION.equals(routeFlag))
        {
            origin = route.getDestination();
        }

        for (Map<String,String> info : coordinates) {
            String destination = info.get("coordinate");
            Map<String, Object> map = new HashMap<>();

            if (ROUTE_TYPE_DESTINATION.equals(routeFlag))
            {
                //终点--家/公司的距离和时间
                map = calDestAndTimeFromTransit(origin, destination,travelType);
            } else if (ROUTE_TYPE_ORIGIN.equals(routeFlag))
            {
                //家/公司---起点和时间
                map = calDestAndTimeFromTransit(destination,origin,travelType);
            }

            String dist = (String) map.get("distance");
            String time = (String) map.get("duration");
            Integer status = (Integer) map.get("status");
            String message = (String) map.get("message");

            CRouteInfo cRouteInfo = new CRouteInfo();

            cRouteInfo.setMemberId(info.get("memberId"));
            cRouteInfo.setDistance(Double.parseDouble(dist));
            cRouteInfo.setDuration(Double.parseDouble(time));
            //地址类型
            cRouteInfo.setAddressType(addressType);
            //出行方式
            cRouteInfo.setTravelType(travelType);
            //起点到公司或者家的距离和时间
            cRouteInfo.setRouteFlag(routeFlag);
            cRouteInfo.setRouteId(route.getId());
            cRouteInfo.setCreateBy(partnerId);
            //保存接口请求返回状态码
            cRouteInfo.setStatus(status);
            //保存接口请求返回状态码描述
            if (Integer.valueOf("2").equals(status))
            {
                cRouteInfo.setMessage("参数无效");
            } else if (Integer.valueOf("1001").equals(status))
            {
                cRouteInfo.setMessage("没有公交方案");
            } else if (Integer.valueOf("1002").equals(status))
            {
                cRouteInfo.setMessage("不支持城市间公交路线");
            } else
            {
                cRouteInfo.setMessage(message);
            }

            BaseModel.preInsert(cRouteInfo);
            cRouteInfoList.add(cRouteInfo);

        }
        try {
            if (cRouteInfoList.size() > 0) {
                routeInfoService.batchInsert(cRouteInfoList);
            }
        }catch (Exception e)
        {
            logger.error("公交计算结果批量插入失败",e);
            return false;
        }

        return true;
    }

    /***
     * 获取距离和时间封装到对应的实体类中
     * @Date 15:38 2019/10/24
     * @param partnerId
     * @param coordnates
     * @param travelType
     * @param route
     * @param routeFlag
     * @param addressType
     * @return
     **/
    private boolean getAllCouteInfo(String partnerId,List<Map<String, String>> coordnates,
                                    Integer travelType, CRoute route,Integer routeFlag,Integer addressType)
    {
        List<CRouteInfo> cRouteInfoList = new ArrayList<>();
        //存放终点坐标串
        List<String> destinations = null;
        //每50个坐标为一组，获取终点坐标串
        destinations = getCoordnates(coordnates);

        //0：路线起点开始 1：路线终点开始
        String origin = "";

        //存放每组坐标串获取的距离和时间
        List<List<Map<String, Object>>> destAndTimeData = new ArrayList<>();
        //获取获取驾驶的距离和时间
        for (String destination : destinations) {
            List<Map<String, Object>> maps = new ArrayList<>();
            if (ROUTE_TYPE_ORIGIN.equals(routeFlag))
            {
                origin = route.getOrigin();
                //公司、家----起点的距离和时间
                maps = driveDistanceAndTime(destination, origin, travelType,routeFlag);
            } else if (ROUTE_TYPE_DESTINATION.equals(routeFlag))
            {
                origin = route.getDestination();
                //终点----公司、家的距离和时间
                maps = driveDistanceAndTime(origin,destination,travelType,routeFlag);
            }

            destAndTimeData.add(maps);
        }
        List<Map<String,Object>> allData = new ArrayList<>();
        //将每组获取的数据，全部存到一个集合中
        for (List<Map<String, Object>> maps : destAndTimeData) {
            for (Map<String, Object> map : maps) {
                allData.add(map);
            }
        }

        //将查询到所有数据进行封装
        try {
            for (int i = 0; i < allData.size(); i++) {
                String dist = (String) allData.get(i).get("distance");
                String time = (String) allData.get(i).get("duration");
                Integer status = (Integer) allData.get(i).get("status");
                String message = (String) allData.get(i).get("message");
                CRouteInfo cRouteInfo = new CRouteInfo();

                Map<String, String> info = coordnates.get(i);

                cRouteInfo.setMemberId(info.get("memberId"));
                cRouteInfo.setDistance(Double.parseDouble(dist));
                cRouteInfo.setDuration(Double.parseDouble(time));
                //地址类型
                cRouteInfo.setAddressType(addressType);
                //出行方式
                cRouteInfo.setTravelType(travelType);
                //起点到公司或者家的距离和时间
                cRouteInfo.setRouteFlag(routeFlag);
                cRouteInfo.setRouteId(route.getId());
                cRouteInfo.setCreateBy(partnerId);
                //保存接口请求返回状态码
                cRouteInfo.setStatus(status);
                //保存接口请求返回状态码描述
                cRouteInfo.setMessage(message);
                BaseModel.preInsert(cRouteInfo);
                cRouteInfoList.add(cRouteInfo);
            }
            if (cRouteInfoList.size() > 0) {
                routeInfoService.batchInsert(cRouteInfoList);
            }

        }catch (Exception e)
        {
            logger.error("批量路算结果插入失败",e);
            return false;
        }
        return true;
    }


    /***
     *  分组获取坐标值
     * @Date 15:39 2019/10/24
     * @param coordnate
     * @return
     **/
    private List<String> getCoordnates(List<Map<String,String>> coordnate)
    {
        List<String> destinations = new ArrayList<>();
        StringBuilder destinationBuilder = new StringBuilder();
        //将坐标转为50个为一组进行计算
        try {
            for (int i = 0 ;i< coordnate.size();i++)
            {
                if (i % 50 == 0 && i != 0)
                {
                    String sub = destinationBuilder.toString().substring(0, destinationBuilder.toString().length() - 1);
                    destinations.add(sub);
                    destinationBuilder = new StringBuilder();
                }
                destinationBuilder.append(coordnate.get(i).get("coordinate")).append("|");

                if (i == coordnate.size()-1)
                {
                    String subs = destinationBuilder.toString().substring(0, destinationBuilder.toString().length() - 1);
                    destinations.add(subs);
                }
            }

        } catch (Exception e)
        {
            logger.error("坐标转为50个一组失败,{}",e);
        }
        return destinations;
    }

    private Map<String,Object> calDestAndTimeFromTransit(String origin,String destination,Integer travelType)
    {
        //结果保留两位小数
        DecimalFormat df =new DecimalFormat("#0.00");
        Map<String, Object> map = new HashMap<>();
        String result = "";
        String requestUrl = "";
        try {
            if (TRAVEL_TYPE_TRANSIT.equals(travelType))
            {
                requestUrl = "http://api.map.baidu.com/directionlite/v1/transit?origin="+origin+"&destination="+destination+"&ak="+ak+"";
            } else if (TRAVEL_TYPE_DIRVE.equals(travelType))
            {
                requestUrl = "http://api.map.baidu.com/directionlite/v1/driving?origin="+origin+"&destination="+destination+"&ak="+ak+"";
            }

            result = sendGet(requestUrl);
            JSONObject json = JSONObject.parseObject(result);
            Integer status = (Integer) json.get("status");
            String message = (String) json.get("message");
            if (Integer.valueOf("0").equals(status))
            {
                Map<String, List<Map<String, Integer>>> result1 = (Map<String, List<Map<String, Integer>>>) json.get("result");
                List<Map<String, Integer>> routes = result1.get("routes");
                Map<String, Integer> desAndTime = routes.get(0);
                Integer dest = desAndTime.get("distance");
                Integer time = desAndTime.get("duration");
                Double destF = Double.parseDouble(dest.toString());
                Double timeF = Double.parseDouble(time.toString());
                map.put("distance",df.format(destF / 1000));
                map.put("duration",df.format(timeF / 60));
                map.put("status",status);
                map.put("message",message);

            } else
            {
                map.put("distance","0");
                map.put("duration","0");
                map.put("status",status);
                map.put("message",message);
                logger.error("调用百度地图公交接口失败,{}",message);
            }
        }catch (Exception e)
        {
            logger.error("调用百度地图公交接口失败,{}",e);
        }
        return map;
    }



    /***
    * 批量计算驾驶和骑行的路程和时间
    * @param origin
    * @param destination
    * @param type
    * @return
    **/
    public List<Map<String, Object>> driveDistanceAndTime(String origin, String destination,Integer type,Integer routeFlag)
    {
        DecimalFormat df =new DecimalFormat("#0.00");
        List<Map<String,Object>> list = new ArrayList<>();
        String result = "";
        String requestUrl = "";
        try {

            if (Integer.valueOf("1").equals(type))
            {
                requestUrl = "http://api.map.baidu.com/routematrix/v2/driving?output=json&tactics=11&origins="+origin+"&destinations="+destination+"&ak="+ak+"";
            } else if (Integer.valueOf("2").equals(type))
            {
                requestUrl ="http://api.map.baidu.com/routematrix/v2/riding?output=json&origins="+origin+"&destinations="+destination+"&ak="+ak+"";
            }

            result = sendGet(requestUrl);
            JSONObject jsonObject = JSONObject.parseObject(result);
            Integer status = (Integer) jsonObject.get("status");
            String message = (String) jsonObject.get("message");
            if (Integer.valueOf("0").equals(status)) {
                List<Map<String,Map<String,Integer>>> result1 = (List<Map<String,Map<String,Integer>>>) jsonObject.get("result");
                Map<String, Map<String, Integer>> stringMapMap1 = result1.get(0);
                Integer resultDistance =  stringMapMap1.get("distance").get("value");
                //没有计算结果
                if (Integer.valueOf("0").equals(resultDistance))
                {
                   //进行单个计算
                    singleCalculation(origin,destination,list,type,routeFlag);
                } else
                {
                    for (Map<String, Map<String, Integer>> stringMapMap : result1)
                    {
                        Map<String, Object> map = new HashMap<>();
                        Map<String, Integer> distanceMap = stringMapMap.get("distance");
                        Map<String, Integer> durationMap = stringMapMap.get("duration");
                        Integer distance = distanceMap.get("value");
                        Integer duration = durationMap.get("value");
                        map.put("distance",df.format(Double.parseDouble(distance.toString()) / 1000));
                        map.put("duration",df.format(Double.parseDouble(duration.toString()) / 60));
                        map.put("status",status);
                        map.put("message",message);
                        list.add(map);
                    }
                }

            } else if (Integer.valueOf("401").equals(status))
            {
                //当前并发量已经超过约定并发配额，限制访问
                //进行单个计算
                singleCalculation(origin,destination,list,type,routeFlag);
            }
            else
            {
                String coodinates = "";
                if (ROUTE_TYPE_ORIGIN.equals(routeFlag))
                {
                    coodinates = origin;
                } else
                {
                    coodinates = destination;
                }
                //批量路算失败，将返回的状态码保存在每个对应坐标对象中
                String[] strings = coodinates.split("\\|");
                for (String string : strings) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("distance","0");
                    map.put("duration","0");
                    map.put("status",status);
                    map.put("message",message);
                    list.add(map);
                }
                logger.error("调用百度地图批量路算接口失败,{}", message);
                logger.error("批量路算接口返回json,{}",jsonObject);
            }
        } catch (Exception e) {
            logger.error("调用百度地图批量路算接口失败,{}", e);
        }
        return list;
    }

    private void singleCalculation(String origin, String destination,List<Map<String,Object>> list,Integer type,Integer routeFlag)
    {
        String coodinates = "";
        String startPonit = "";
        if (ROUTE_TYPE_ORIGIN.equals(routeFlag))
        {
            //起点，分解origin
            coodinates = origin;
            //计算家、公司到起点的距离和时间
            startPonit = destination;
        } else if (ROUTE_TYPE_DESTINATION.equals(routeFlag))
        {
            //终点，分解destination
            coodinates = destination;
            //计算终点到家公司的距离和时间
            startPonit = origin;
        }
        //坐标分解，进行单个查询
        String[] destinations = coodinates.split("\\|");
        for (String dest : destinations)
        {
            Map<String, Object> map = calDestAndTimeFromTransit(startPonit, dest, type);
            list.add(map);
        }
    }

    /**
     * 获取四条路线
     *
     * @param routeFlag   路线标记(0：表示路线起点 1：表示路线终点)
     * @param addressType 地址类型(0：公司地址 1：家地址)
     * @return list集合
     * @author yifeng
     * @date 15:49 2019/11/1
     **/
    public List<Map<Integer, Integer>> getRouteTypeList(Integer routeFlag, Integer addressType) {
        Integer routeFlagStart = 0;
        Integer routeFlagEnd = 1;

        Integer addressTypeCompany = 0;
        Integer addressTypeHome = 1;


        List<Map<Integer, Integer>> typeList = Lists.newArrayList();
        Map<Integer, Integer> map1 = Maps.newHashMap();
        map1.put(addressTypeHome, routeFlagStart);

        Map<Integer, Integer> map2 = Maps.newHashMap();
        map2.put(addressTypeCompany, routeFlagStart);

        Map<Integer, Integer> map3 = Maps.newHashMap();
        map3.put(addressTypeHome, routeFlagEnd);

        Map<Integer, Integer> map4 = Maps.newHashMap();
        map4.put(addressTypeCompany, routeFlagEnd);

        if (null != addressType && null == routeFlag) {
            if (addressTypeCompany.equals(addressType)) {
                typeList.add(map2);
                typeList.add(map4);
            } else if (addressTypeHome.equals(addressType)) {
                typeList.add(map1);
                typeList.add(map3);
            }
        }
        if (null != routeFlag && null == addressType) {
            if (routeFlagStart.equals(routeFlag)) {
                typeList.add(map1);
                typeList.add(map2);
            } else if (routeFlagEnd.equals(routeFlag)) {
                typeList.add(map3);
                typeList.add(map4);
            }
        }
        if (null == addressType && null == routeFlag) {
            typeList.add(map1);
            typeList.add(map2);
            typeList.add(map3);
            typeList.add(map4);
        }
        if (null != addressType && null != routeFlag) {
            if (addressTypeCompany.equals(addressType) && routeFlagStart.equals(routeFlag)) {
                typeList.add(map2);
            }
            if (addressTypeCompany.equals(addressType) && routeFlagEnd.equals(routeFlag)) {
                typeList.add(map4);
            }
            if (addressTypeHome.equals(addressType) && routeFlagStart.equals(routeFlag)) {
                typeList.add(map1);
            }
            if (addressTypeHome.equals(addressType) && routeFlagEnd.equals(routeFlag)) {
                typeList.add(map3);
            }
        }
        return typeList;
    }
}
